package com.yum.Iterator.Table;

import com.yum.Iterator.Collection.YCollection;
import com.yum.Iterator.Enumeration.YEnumeration;
import com.yum.Iterator.Map.YMap;
import com.yum.Iterator.Set.YSet;
import com.yum.Iterator.YIterator;
import com.yum.lang.YInteger;

import java.io.Serializable;
import java.util.Objects;

/**
 * Created on 2023/6/8
 *
 * @Time 20:25
 * @Program YunMap
 * @Author YoHaNe/XiaoShuai
 */
public class YHashTable<K,V> extends YDictionary<K,V>
        implements YMap<K,V>,Cloneable, Serializable {

    private static final long serialVersionUID = 1971135080823210870L;
    /**
     * hashtable data
     */
    private transient YEntry[] table;

    /**
     * 表中键值对的数量
     */
    private transient int count;

    /**
     * 阈(yu)值,用于判断是否扩容(threshold = 容量*加载因子)
     */
    private int threshold;

    /**
     * 加载因子
     */
    private float loadFactor;

    /**
     * 修改次数 fail-fast
     */
    private transient int modCount = 0;

    /**
     * 最大容量大小
     * 一些虚拟机会保存头字（头信息）,尝试分配更大的容量的会导致 OutOfMemoryError错误 会超过VM的限制
     */
    private static final int MAX_ARRAY_SIZE = YInteger.MAX_VALUE - 8;


    public YHashTable(){
        this(11,0.75f);
    }

    public YHashTable(int initialCapacity){
        this(initialCapacity,0.75f);
    }

    public YHashTable(int initialCapacity,float loadFactor){
        if(initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
        if(loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal LoadFactor: " + loadFactor);
        initialCapacity = initialCapacity == 0 ? 1 : initialCapacity;
        this.loadFactor = loadFactor;
        table = new YEntry[initialCapacity];
        threshold = (int) Math.min(initialCapacity * loadFactor,MAX_ARRAY_SIZE - 1);
    }

    private static class YEntry<K,V> implements YMap.YEntry<K,V>{

        final int hash;
        final K key;
        V value;
        YEntry<K,V> next;

        public YEntry(int hash, K key, V value, YEntry<K, V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        @SuppressWarnings("unchecked")
        protected Object clone(){
            return new YEntry<K,V>(hash,key,value,next == null ? null : (YEntry<K, V>) next.clone());
        }

        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(V value) {
            if(key == null)
                throw new NullPointerException();
            V old = this.value;
            this.value = value;
            return old;
        }

        @Override
        public boolean equals(Object obj) {
            if(!(obj instanceof YMap.YEntry))
                return false;
            YMap.YEntry<?,?> e = (YMap.YEntry<?,?>)obj;
            return (key == null ? e.getKey() == null : key.equals(e.getKey()) &&
                    value == null ? e.getValue() == null : value.equals(e.getValue()));
        }

        @Override
        public int hashCode() {
            return hash ^ Objects.hashCode(value);
        }

        @Override
        public String toString() {
            return key.toString() + "=" + value.toString();
        }
    }

    @Override
    public YCollection<V> values() {
        return null;
    }

    @Override
    public void putAll(YMap<? extends K, ? extends V> m) {

    }

    @SuppressWarnings("unchecked")
    @Override
    public V get(K key) {
        YEntry<?,?>[] tab = table;
        int hash = key.hashCode();
        int index = (hash & YInteger.MAX_VALUE) % tab.length;
        for(YEntry<?,?> e = tab[index];e != null;e = e.next){
            if((e.hash == hash) && (e.key.equals(key))){
                return (V) e.value;
            }
        }
        return null;
    }



    @SuppressWarnings("unchecked")
    private void addEntry(int hash,K key,V val,int index){
        modCount++;
        YEntry<?,?>[] tab = table;
        //判断是否需要扩容
        if(count >= threshold){
            rehash();
            //扩容完毕重新计算
            tab = table;
            hash = key.hashCode();
            index = (hash & YInteger.MAX_VALUE) % tab.length;
        }
        YEntry<K,V> e = (YEntry<K, V>) tab[index];
        tab[index] = new YEntry<>(hash,key,val,e);
        count++;
    }

    /**
     * 扩容/调整hash表
     *
     */
    @SuppressWarnings("unchecked")
    protected void rehash(){
        int oldCapacity = table.length;
        YEntry<?,?>[] oldTab = table;
        //扩容
        int newCapacity = (oldCapacity << 1) + 1;
        if(newCapacity - MAX_ARRAY_SIZE > 0){
            if(oldCapacity == MAX_ARRAY_SIZE)
                //使用MAX_ARRAY_SIZE存储桶保持运行
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }
        YEntry<?,?>[] newTab = new YEntry<?,?>[newCapacity];
        modCount++;
        threshold = (int) Math.min(newCapacity * loadFactor,MAX_ARRAY_SIZE + 1);
        table = newTab;
        //将旧数据放入新表中
        for(int i = oldCapacity;i-- > 0 ;){
            for(YEntry<K,V> old = (YEntry<K, V>) oldTab[i];old != null;){
                YEntry<K,V> e = old;
                old = old.next;
                int index = (e.hash & YInteger.MAX_VALUE) % newCapacity;
                e.next = (YEntry<K, V>) newTab[index];
                newTab[index] = e;
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public synchronized V put(K key, V value) {
        if(value == null)
            throw new NullPointerException();
        YEntry<?,?>[] tab = table;
        int hash = key.hashCode();
        //根据hashCore和表长度来决定索引位置
        int index = (hash & YInteger.MAX_VALUE) % tab.length;
        YEntry<K,V> entry = (YEntry<K, V>) tab[index];
        //判断key是否已经存在
        for(;entry != null;entry = entry.next){
            if((entry.hash == hash) && (entry.key == key)){
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }
        addEntry(hash,key,value,index);
        return null;
    }

    @Override
    public void clear() {

    }

    @SuppressWarnings("unchecked")
    @Override
    public synchronized V remove(K key) {
        YEntry<?,?>[] tab = table;
        int hash = key.hashCode();
        int index = (hash & YInteger.MAX_VALUE) % tab.length;
        YEntry<K,V> e = (YEntry<K, V>) tab[index];
        for(YEntry<K,V> prev = null;e != null; prev = e,e = e.next){
            if((hash == e.hash) && e.key.equals(key)){
                modCount++;
                if(prev != null)
                    prev.next = e.next;
                else
                    tab[index] = e.next;
                count--;
                V oldVal = e.value;
                e.value = null;
                return oldVal;
            }
        }
        return null;
    }

    @Override
    public boolean containsKey(K key) {
        YEntry<?,?>[] tab = table;
        int hash = key.hashCode();
        int index = (hash & YInteger.MAX_VALUE) % tab.length;
        for(YEntry<?,?> e = tab[index];e != null;e = e.next){
            if((e.hash == hash) && (e.key.equals(key))){
                return true;
            }
        }
        return false;
    }

    public synchronized boolean contains(Object value){
        if(value == null)
            throw new NullPointerException();
        YEntry<?,?>[] tab = table;
        for(int i = tab.length; i-- > 0;){
            for(YEntry<?,?> e = tab[i]; e != null;e = e.next){
                if(e.value.equals(value))
                    return true;
            }
        }
        return false;
    }

    @Override
    public boolean contatinsValue(V value) {
        return contains(value);
    }

    @Override
    public YSet<K> keySet() {
        return null;
    }

    @Override
    public YSet<YMap.YEntry<K, V>> entrySet() {
        return null;
    }


    @Override
    public int size() {
        return count;
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public YEnumeration<K> keys() {
        return null;
    }

    @Override
    public YEnumeration<V> elements() {
        return null;
    }

    @Override
    public V get() {
        return null;
    }

    @Override
    public V put() {
        return null;
    }

    @Override
    public V remove() {
        return null;
    }

    @Override
    public synchronized String toString() {
        int max = size() - 1;
        if(max == -1)
           return "{}";
        StringBuilder sb = new StringBuilder();
        return null;
    }
}
